package authcomment

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"strconv"
	"strings"
	"sync"
	"time"

	"blogcenter/internal/biz"
	"blogcenter/internal/middleware"
	"blogcenter/internal/model"
	"blogcenter/internal/svc"
	"blogcenter/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/core/stores/redis"
	"gorm.io/gorm"
)

type GetCommentsLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
	CommentBlogLogic
}

func NewGetCommentsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetCommentsLogic {
	return &GetCommentsLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

// SubjectInfo 评论主题信息
type SubjectInfo struct {
	Total     int `json:"total"`     // 总评论数
	RootCount int `json:"rootCount"` // 根评论数
}

var redisTimeout = 3

func (l *GetCommentsLogic) GetComments(req *types.CommentQueryRequest) (*types.CommentListResponse, error) {
	// 1. 参数校验
	userId, _ := l.ctx.Value(middleware.UserIdKey).(int)
	if err := l.validateParams(req); err != nil {
		return nil, &biz.BizError{
			Code:    400,
			Message: err.Error(),
		}
	}

	// 2. 构建主题缓存Key
	subjectKey := fmt.Sprintf("comment_subject:%d:%d", req.ObjectType, req.ObjectID)

	// 3. 获取主题信息
	subjectInfo, err := l.getSubjectInfo(subjectKey, req.ObjectType, req.ObjectID)
	if err != nil {
		return nil, errors.New("获取主题信息失败" + err.Error())
	}

	// 4. 获取一级评论
	rootComments, err := l.getRootComments(req)
	if err != nil {
		return nil, errors.New("获取一级评论失败" + err.Error())
	}
	// 获取每个子评论的点赞状态（批量处理）
	for i := range rootComments {
		reply := rootComments[i]
		// 查询是否点赞过该子评论
		isLiked, err := l.getIsLikeComment(userId, int(reply.ID))
		if err != nil {
			logx.WithContext(l.ctx).Errorf("获取子评论点赞状态失败: %v", err)
			continue
		}
		reply.IsLiked = isLiked
	}

	totalPage := int(math.Ceil(float64(subjectInfo.Total) / float64(req.PageSize)))
	// 5. 构建响应
	return &types.CommentListResponse{
		BaseResponse: types.BaseResponse{
			Code:    200,
			Message: "success",
		},
		Total:       subjectInfo.Total,
		RootCount:   subjectInfo.RootCount,
		CurrentPage: req.Page,
		TotalPage:   totalPage,
		Comments:    rootComments,
	}, nil
}

// 参数校验
func (l *GetCommentsLogic) validateParams(req *types.CommentQueryRequest) error {
	if req.ObjectID == 0 {
		return errors.New("objectId不能为空")
	}
	if req.ObjectType < 0 || req.ObjectType > 1 {
		return errors.New("无效的对象类型")
	}
	if req.Page <= 0 {
		req.Page = 1
	}
	if req.PageSize <= 0 || req.PageSize > 100 {
		req.PageSize = 20
	}
	if req.Sort != "newest" && req.Sort != "hot" {
		req.Sort = "newest"
	}
	return nil
}

// 获取主题信息
func (l *GetCommentsLogic) getSubjectInfo(key string, objType int, objId int) (*SubjectInfo, error) {
	// 1. 检查键是否存在
	exists, err := l.svcCtx.Redis.Exists(key)
	if err != nil {
		return nil, fmt.Errorf("检查主题缓存失败: %w", err)
	}
	if !exists {
		return l.loadSubjectFromDB(key, objType, objId)
	}

	// 2. 获取并验证数据
	data, err := l.svcCtx.Redis.Get(key)
	if err != nil {
		return nil, fmt.Errorf("获取主题缓存失败: %w", err)
	}
	if data == "" {
		return nil, errors.New("主题缓存数据为空")
	}

	// 3. 严格解析JSON
	var info SubjectInfo
	decoder := json.NewDecoder(strings.NewReader(data))
	decoder.DisallowUnknownFields()
	if err := decoder.Decode(&info); err != nil {
		// 自动修复损坏缓存
		_, _ = l.svcCtx.Redis.Del(key)
		return l.loadSubjectFromDB(key, objType, objId)
	}

	return &info, nil
}

// 从数据库加载主题信息
func (l *GetCommentsLogic) loadSubjectFromDB(key string, objType int, objId int) (*SubjectInfo, error) {
	// 获取数据库数据
	total, err := l.svcCtx.CommentModel.CountByObject(l.ctx, objType, uint(objId))
	if err != nil {
		return nil, fmt.Errorf("获取总评论数失败: %w", err)
	}

	rootCount, err := l.svcCtx.CommentModel.CountRootComments(l.ctx, objType, uint(objId))
	if err != nil {
		return nil, fmt.Errorf("获取根评论数失败: %w", err)
	}

	info := &SubjectInfo{
		Total:     int(total),
		RootCount: int(rootCount),
	}

	// 严格序列化JSON
	buf := new(bytes.Buffer)
	encoder := json.NewEncoder(buf)
	encoder.SetEscapeHTML(false)
	if err := encoder.Encode(info); err != nil {
		return nil, fmt.Errorf("序列化主题信息失败: %w", err)
	}

	// 存储到Redis（设置过期时间）
	if err := l.svcCtx.Redis.Setex(key, buf.String(), redisTimeout); err != nil {
		logx.Errorf("更新主题缓存失败: %v", err)
	}

	return info, nil
}

func (l *GetCommentsLogic) batchBuildComments(commentIDs []string, objectID uint) ([]*types.CommentResponse, error) {
	var comments []*types.CommentResponse
	var wg sync.WaitGroup
	var mu sync.Mutex
	sem := make(chan struct{}, 10) // 限制并发数

	for _, idStr := range commentIDs {
		wg.Add(1)
		go func(idStr string) {
			defer wg.Done()
			sem <- struct{}{}
			defer func() { <-sem }()

			id, err := strconv.ParseUint(idStr, 10, 64)
			if err != nil {
				logx.WithContext(l.ctx).Errorf("解析评论ID失败: %v", err)
				return
			}

			comment, err := l.buildCommentResponse(uint(id), objectID)
			if err != nil {
				logx.WithContext(l.ctx).Errorf("构建评论响应失败: %v", err)
				return
			}

			mu.Lock()
			comments = append(comments, comment)
			mu.Unlock()
		}(idStr)
	}

	wg.Wait()
	return comments, nil
}

// 修改 getRootComments 方法
func (l *GetCommentsLogic) getRootComments(req *types.CommentQueryRequest) ([]*types.CommentResponse, error) {
	// 1. 首先尝试从Redis获取
	comments, err := l.getFromRedis(req)
	if err != nil || len(comments) == 0 {
		// 2. Redis无数据时回退到数据库
		comments, err = l.getFromDB(req)
		l.fullIsAuthorAndUser(comments, req.ObjectID)
		if err != nil {
			return nil, fmt.Errorf("获取评论失败: %w", err)
		}

		// 3. 异步更新Redis缓存
		go l.updateRedisCache(req, comments)
	}

	return comments, nil
}

// 完善 IsAuthor 和 User 信息
func (l *GetCommentsLogic) fullIsAuthorAndUser(comments []*types.CommentResponse, objectID int) {
	for _, comment := range comments {
		isAuthor, err := l.checkIsAuthor(comment.UserID, uint(objectID))
		if err != nil {
			logx.WithContext(l.ctx).Errorf("获取评论作者失败: %v", err)
			continue
		}
		comment.IsAuthor = isAuthor

		user, err := l.getUserInfo(comment.UserID)
		if err != nil {
			logx.WithContext(l.ctx).Errorf("获取用户信息失败: %v", err)
			continue
		}
		comment.UserName = user.Name
		comment.Avatar = user.Avatar
	}
}

// 从Redis获取评论
func (l *GetCommentsLogic) getFromRedis(req *types.CommentQueryRequest) ([]*types.CommentResponse, error) {
	zsetKey := fmt.Sprintf("comment_index:%s:%d:%d:0",
		getSortKey(req.Sort),
		req.ObjectType,
		req.ObjectID)

	start := (req.Page - 1) * req.PageSize
	end := start + req.PageSize - 1

	commentIDs, err := l.svcCtx.Redis.Zrevrange(zsetKey, int64(start), int64(end))
	if err != nil {
		return nil, err
	}

	return l.batchBuildComments(commentIDs, uint(req.ObjectID))
}

// 修改 getFromDB 方法，使用正确的 GORM 调用方式
func (l *GetCommentsLogic) getFromDB(req *types.CommentQueryRequest) ([]*types.CommentResponse, error) {
	// 调用模型层方法
	indexes, err := l.svcCtx.CommentModel.FindRootComments(
		l.ctx,
		req.ObjectType,
		uint(req.ObjectID),
		int(req.Page),
		int(req.PageSize),
		req.Sort,
	)
	if err != nil {
		return nil, fmt.Errorf("查询根评论失败: %w", err)
	}

	// 调用模型层转换方法
	return l.svcCtx.CommentModel.ConvertToResponse(l.ctx, indexes, uint(req.ObjectID))
}

func (l *GetCommentsLogic) updateRedisCache(req *types.CommentQueryRequest, comments []*types.CommentResponse) {
	// 1. 更新评论内容缓存
	for _, comment := range comments {
		contentKey := fmt.Sprintf("comment_content:%d", comment.ID)
		l.svcCtx.Redis.Setex(contentKey, comment.Content, redisTimeout)
	}

	// 2. 更新索引有序集合
	tsKey := fmt.Sprintf("comment_index:ts:%d:%d:0", req.ObjectType, req.ObjectID)
	likeKey := fmt.Sprintf("comment_index:like:%d:%d:0", req.ObjectType, req.ObjectID)

	for _, comment := range comments {
		// 时间索引
		l.svcCtx.Redis.Zadd(tsKey, int64(time.Now().Unix()), strconv.Itoa(int(comment.ID)))

		// 点赞索引
		l.svcCtx.Redis.Zadd(likeKey, int64(comment.LikeCount), strconv.Itoa(int(comment.ID)))
	}

	// 设置过期时间
	l.svcCtx.Redis.Expire(tsKey, redisTimeout)
	l.svcCtx.Redis.Expire(likeKey, redisTimeout)
}

// 辅助函数
func getSortKey(sort string) string {
	if sort == "hot" {
		return "like"
	}
	return "ts"
}

// 构建单个评论响应
func (l *GetCommentsLogic) buildCommentResponse(commentID uint, objectID uint) (*types.CommentResponse, error) {
	// 获取评论索引
	index, err := l.getCommentIndex(commentID)
	if err != nil {
		return nil, err
	}

	// 获取评论内容
	content, err := l.getCommentContent(commentID)
	if err != nil {
		return nil, err
	}

	// 获取用户信息
	user, err := l.getUserInfo(index.UserId)
	if err != nil {
		return nil, err
	}

	// 检查是否有回复
	hasReply, err := l.checkHasReply(commentID)
	if err != nil {
		return nil, err
	}

	// 检查是否是作者
	isAuthor, err := l.checkIsAuthor(index.UserId, objectID)
	if err != nil {
		return nil, err
	}

	return &types.CommentResponse{
		ID:         commentID,
		Content:    content,
		UserID:     index.UserId,
		UserName:   user.Name,
		Avatar:     user.Avatar,
		CreateTime: index.CreatedAt.Format(time.RFC3339),
		LikeCount:  index.LikeCount,
		IsReply:    hasReply,
		IsAuthor:   isAuthor,
	}, nil
}

// 获取评论索引
func (l *GetCommentsLogic) getCommentIndex(commentID uint) (*model.CommentIndex, error) {
	// 先从缓存获取
	index, err := l.svcCtx.CommentModel.FindIndex(l.ctx, commentID)
	if err != nil {
		return nil, fmt.Errorf("获取评论索引失败: %w", err)
	}
	return index, nil
}

// 获取评论内容
func (l *GetCommentsLogic) getCommentContent(commentID uint) (string, error) {
	// 先从缓存获取
	key := fmt.Sprintf("comment_content:%d", commentID)
	content, err := l.svcCtx.Redis.Get(key)
	if err == redis.Nil {
		// 缓存未命中，从数据库获取
		contentModel, err := l.svcCtx.CommentModel.FindContent(l.ctx, commentID)
		if err != nil {
			return "", fmt.Errorf("获取评论内容失败: %w", err)
		}

		// 更新缓存
		if err := l.svcCtx.Redis.Setex(key, contentModel.Content, redisTimeout); err != nil {
			logx.WithContext(l.ctx).Errorf("更新评论内容缓存失败: %v", err)
		}

		return contentModel.Content, nil
	}
	if err != nil {
		return "", fmt.Errorf("获取评论内容缓存失败: %w", err)
	}

	return content, nil
}

// 获取用户信息

// 检查是否有回复
func (l *GetCommentsLogic) checkHasReply(commentID uint) (int, error) {
	// 获取评论索引以确定对象类型和ID
	index, err := l.svcCtx.CommentModel.FindIndex(l.ctx, commentID)
	if err != nil {
		return 0, err
	}

	// 检查时间索引集合是否有成员
	key := fmt.Sprintf("comment_index:ts:%d:%d:%d", index.ObjectType, index.ObjectId, commentID)
	count, err := l.svcCtx.Redis.Zcard(key)
	if err != nil {
		return count, fmt.Errorf("检查回复失败: %w", err)
	}

	return count, nil
}

// 检查是否是作者
func (l *GetCommentsLogic) checkIsAuthor(userID int, objectID uint) (bool, error) {
	// 实现根据业务逻辑检查
	// 示例：调用博客服务检查作者身份
	isAuthor, err := l.svcCtx.BlogModel.CheckAuthor(l.ctx, objectID, userID)
	if err != nil {
		return false, fmt.Errorf("检查作者身份失败: %w", err)
	}
	return isAuthor, nil
}

func (l *GetCommentsLogic) getIsLikeComment(userId int, commentId int) (bool, error) {
	var isLiked bool
	if userId < 0 {
		return false, errors.New("用户ID不能小于0")
	}
	// 先尝试从Redis获取点赞状态
	likeKey := fmt.Sprintf("%s%d:%d", commentLikeKeyPrefix, commentId, userId)
	if val, err := l.svcCtx.Redis.Get(likeKey); err == nil && val == "1" {
		isLiked = true
	} else if err != nil && err != redis.Nil {
		logx.Errorf("获取Redis点赞状态失败, key: %s, error: %v", likeKey, err)
	}

	// Redis未命中则查询数据库
	if !isLiked {
		var likeRecord model.CommentLike
		err := l.svcCtx.DB.Where("comment_index_id = ? AND user_id = ?", commentId, userId).
			First(&likeRecord).Error
		if err == nil && likeRecord.LikeAction == 1 {
			isLiked = true
			// 异步写入Redis缓存
			go func() {
				if err := l.svcCtx.Redis.Setex(likeKey, "1", int((24 * time.Hour).Seconds())); err != nil {
					logx.Errorf("写入点赞状态到Redis失败, key: %s, error: %v", likeKey, err)
				}
			}()
		} else if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
			logx.Errorf("查询数据库点赞状态失败, blogId: %d, userId: %d, error: %v",
				commentId, userId, err)
			return false, err
		}
	}
	return isLiked, nil
}
